package com.pt25.study.example.interfaces.facade.test;

import com.pt25.base.constants.BaseConstants;
import com.pt25.base.model.websocket.WebsocketHandler;
import com.pt25.base.res.ResBody;
import com.pt25.base.util.WsClientUtil1;
import com.pt25.base.util.WsClientUtil2;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.websocket.CloseReason;
import jakarta.websocket.Session;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okio.ByteString;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Tag(name = "websocket test api")
@RestController
@RequestMapping("/test-api/example")
public class WsTestController implements WebsocketHandler {
    private final static String SEND_WS_MSG1 = "/send-ws-msg1";
    private final static String SEND_WS_MSG2 = "/send-ws-msg2";

    @Override
    public void handleTextMessage(ChannelHandlerContext ctx, TextWebSocketFrame msg) {
        log.info("server 收到客户端msg:{}", msg.text());
        //发送文本数据
        ctx.writeAndFlush(new TextWebSocketFrame("hello client friend!"));
        //发送二进制数据
        //创建一个初始容量为10字节，但可以自动扩展的ByteBuf
        ByteBuf buf = Unpooled.buffer(10);
        buf.writeBytes("hello client friend! binary".getBytes());
        ctx.writeAndFlush(new BinaryWebSocketFrame(buf));
    }

    @Override
    public void handleBinaryMessage(ChannelHandlerContext channelHandlerContext, BinaryWebSocketFrame binaryWebSocketFrame) {
        log.info("server handleBinaryMessage");
    }

    @Override
    public void handleErrorMessage(ChannelHandlerContext ctx, Throwable cause) {
        log.info("server handleErrorMessage");
    }

    @Override
    public void handleChannelActive(ChannelHandlerContext ctx) {
        log.info("server handleChannelActive");
    }

    @Override
    public void handleChannelInactive(ChannelHandlerContext ctx) {
        log.info("server handleChannelInactive");
    }

    @Override
    public Boolean checkAuthorization(ChannelHandlerContext ctx, FullHttpRequest request) {
        log.info("server checkAuthorization");
        return true;
    }

    @Override
    public void handleHeartbeat(ChannelHandlerContext ctx, Integer status) {

    }


    @GetMapping(SEND_WS_MSG1)
    public ResBody sendWSMsg3() {
        Map<String, String> headers = new HashMap<>();
        headers.put(BaseConstants.AUTHORIZATION, "Bearer your_token");
        headers.put("Custom-Header", "HeaderValue");
        WsClientUtil1.WSConfig wsConfig = new WsClientUtil1.WSConfig();
        wsConfig.setWebSocketUrl("ws://localhost:4444/ws");
        wsConfig.setHeaders(headers);
        WsClientUtil1.WebSocketClientPool pool = WsClientUtil1.getWebsocketClientPool(wsConfig);
        for (int j = 0; j < 20; j++) {
            new Thread(() -> {
                try {
                    WsClientUtil1.WebSocketClientEndpoint client = pool.borrowClient();
                    log.info("client:{}", client);
                    client.setMessageHandler(new WsClientUtil1.WebSocketClientEndpoint.MessageHandler() {
                        @Override
                        public void handleMessage(String message) {
                            log.info("message:{}", message);
                        }

                        @Override
                        public void handleMessage(ByteBuffer bytes) {
                            log.info("bytes:{}", new String(bytes.array()));
                        }

                        @Override
                        public void handleError(Session session, Throwable thr) {

                        }

                        @Override
                        public void handleClose(Session session, CloseReason closeReason) {

                        }
                    });
                    client.sendMessage("Hello, WebSocket");
                    Thread.sleep(100);
                    pool.returnClient(client);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }).start();
        }
        return ResBody.builder().build().ok();
    }

    @GetMapping(SEND_WS_MSG2)
    public ResBody sendWSMsg2() {
        Map<String, String> headers = new HashMap<>();
        headers.put(BaseConstants.AUTHORIZATION, "Bearer your_token");
        headers.put("Custom-Header", "HeaderValue");
        WsClientUtil2.WSConfig wsConfig = new WsClientUtil2.WSConfig();
        wsConfig.setWebSocketUrl("ws://localhost:4444/ws");
        wsConfig.setHeaders(headers);
        GenericObjectPool<WsClientUtil2.WebSocketClient> pool = WsClientUtil2.getWebsocketClientPool(wsConfig);
        for (int j = 0; j < 20; j++) {
            new Thread(() -> {
                try {
                    WsClientUtil2.WebSocketClient client = pool.borrowObject();
                    log.info("client:{}", client);
                    client.setMessageHandler(new WsClientUtil2.MessageHandler() {
                        @Override
                        public void handleMessage(WebSocket webSocket, String message) {
                            log.info("message:{}", message);
                        }

                        @Override
                        public void handleMessage(WebSocket webSocket, ByteString message) {
                            log.info("bytes:{}", new String(message.toByteArray()));
                        }

                        @Override
                        public void handleOpen(WebSocket webSocket, Response response) {

                        }

                        @Override
                        public void handleClosing(WebSocket webSocket, int code, String reason) {

                        }

                        @Override
                        public void handleClosed(WebSocket webSocket, int code, String reason) {

                        }

                        @Override
                        public void handleFailure(WebSocket webSocket, Throwable t, Response response) {

                        }
                    });
                    client.sendMessage("Hello, WebSocket");
                    Thread.sleep(100);
                    pool.returnObject(client);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }).start();
        }
        return ResBody.builder().build().ok();
    }

    /**
     * 1、MyWebSocketListener必须是线程安全的！！！！
     * 2、MyWebSocketListener如果持有外部对象的引用，一定要在socket回调的断开的方法和失败的方法中释放引用
     */
    @Slf4j
    private static class MyWebSocketListener extends WebSocketListener {
        @Override
        public void onClosed(@NotNull WebSocket webSocket, int code, @NotNull String reason) {
            log.info("client onClosed code:{} reason:{}", code, reason);
            //释放外部对象引用
        }

        @Override
        public void onClosing(@NotNull WebSocket webSocket, int code, @NotNull String reason) {
            log.info("client onClosing code:{} reason:{}", code, reason);
        }

        @Override
        public void onFailure(@NotNull WebSocket webSocket, @NotNull Throwable t, @Nullable Response response) {
            log.info("client onFailure error:{} response:{}", t, response);
            //释放外部对象引用
        }

        @Override
        public void onMessage(@NotNull WebSocket webSocket, @NotNull String text) {
            log.info("client onMessage text：{}", text);
        }

        @Override
        public void onMessage(@NotNull WebSocket webSocket, @NotNull ByteString bytes) {
            log.info("client onMessage bytes：{}", bytes);
        }

        @Override
        public void onOpen(@NotNull WebSocket webSocket, @NotNull Response response) {
            log.info("client onOpen");
        }
    }

}


